package com.icloud.wallet.boot.utils;

import com.alibaba.fastjson.JSON;
import io.github.novacrypto.bip39.MnemonicGenerator;
import io.github.novacrypto.bip39.SeedCalculator;
import io.github.novacrypto.bip39.Words;
import io.github.novacrypto.bip39.wordlists.English;
import io.github.novacrypto.hashing.Sha256;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.FunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.abi.datatypes.generated.Uint8;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.tx.ChainId;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;
import party.loveit.bip44forjava.utils.Bip44Utils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 方法说明: 工具类，web3j需要使用的BNB-RPC工具类
 * 传入参数:
 * 返回的值:
 * 作者: java-101
 * 日期: 2024-05-21 16:22:12
 */
@Slf4j
public class BNBWeb3jApiUtil {
    /**
     * 方法说明: 补位，不足使用0进行补位
     * 传入参数:
     * 返回的值:
     * 作者: java-101
     * 日期: 2024-05-21 16:22:18
     */
    public static String fillZero(String input) {
        String str = input.replace("0x", "");

        int strLen = input.length();
        StringBuffer sb = null;
        while (strLen < 64) {
            sb = new StringBuffer();
            sb.append("0").append(str);// 左补0
            str = sb.toString();
            strLen = str.length();
        }
        return str;
    }

    /**
     * <p>方法说明: 将hex字符串按照18位小数转数字
     * <p>参数说明:
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 13:29
     * <p>创  建  人: java-101
    **/
    public static BigDecimal stringToDBNum(String hexString) {
        hexString = hexString.substring(2);
        BigInteger bigInteger = new BigInteger(hexString, 16);
        BigDecimal multiply = new BigDecimal(bigInteger).divide(new BigDecimal(10).pow(18));
        return multiply;
    }

    /**
     * <p>方法说明: 将hex字符串按照指定位小数转数字
     * <p>参数说明:
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 13:29
     * <p>创  建  人: java-101
    **/
    public static BigDecimal stringToDBNum(String hexString, int decimals) {
        hexString = hexString.substring(2);
        BigInteger bigInteger = new BigInteger(hexString, 16);
        BigDecimal multiply = new BigDecimal(bigInteger).divide(new BigDecimal(10).pow(decimals));
        return multiply;
    }

    /**
     * <p>方法说明: 将数字字符串类型转为18为BigDecimal数字
     * <p>参数说明:
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 13:30
     * <p>创  建  人: java-101
    **/
    public static BigDecimal toNum_isNumber(String numberStr) {
        BigDecimal multiply = new BigDecimal(numberStr).divide(new BigDecimal(10).pow(18));
        return multiply;
    }

    /**
     * <p>方法说明: 将hexString字符串转为数字
     * <p>参数说明:
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 13:31
     * <p>创  建  人: java-101
    **/
    public static BigInteger toNum(String hexString) {
        hexString = hexString.substring(2);
        return new BigInteger(hexString, 16);
    }

    /**
     * <p>方法说明: 将数字转为Hex字符串
     * <p>参数说明:
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 13:31
     * <p>创  建  人: java-101
    **/
    public static String toHexString(BigInteger numString) {
        return "0x" + numString.toString(16);
    }

    public static String dbNum2BnbNum(BigDecimal dbNum, int decimals) {
        BigDecimal multiply = dbNum.multiply(new BigDecimal(10).pow(decimals));
        log.info("dbNum2BnbNum::{}", multiply.toBigInteger());
        return toHexString(multiply.toBigInteger());
    }

    public static BigDecimal dbGasPrice2BnbNum(int dbNum) {
        BigDecimal bigDecimal = new BigDecimal(dbNum);
        BigDecimal multiply = bigDecimal.multiply(new BigDecimal(10).pow(9));
        log.info("dbGasPrice2BnbNum::{}", multiply.toBigInteger());
        return multiply;
    }

    public static String dbGasPrice2BnbGasPrice(int dbNum) {
        BigDecimal bigDecimal = new BigDecimal(dbNum);
        BigDecimal divide = bigDecimal.multiply(new BigDecimal(10).pow(9));
        log.info("dbGasPrice2BnbGasPrice::{}", divide.toBigInteger());
        return toHexString(divide.toBigInteger());
    }

    public static int bnbGasPrice2DbGasPrice(String bnbGasPrice) {
        BigInteger bigInteger = toNum(bnbGasPrice);
        BigInteger divide = bigInteger.divide(new BigInteger("10").pow(9));
        log.info("bnbGasPrice2DbGasPrice::{}", divide.intValue());
        return divide.intValue();
    }


    public static String getridof_zero_address(String input) throws Throwable {
        if (input.length() < 40) {
            return null;
        }
        String str = input.substring(input.length() - 40, input.length());
        return "0x" + str;
    }

    public static String getridof_zero(String input) throws Throwable {
        String str = input.replaceFirst("^0*", "");
        return "0x" + str;
    }

    /**
     * <p>方法说明: 获取账户的Nonce
     * <p>参数说明: addr 地址
     * <p>返回说明: 返回指定地址发生的交易数量
     * <p>创建时间: 2020/6/17 0017 19:04
     * <p>创  建  人: java-101
    **/
    public static BigInteger getNonce(Web3j web3j, String addr) {
        try {
            EthGetTransactionCount getNonce = web3j.ethGetTransactionCount(addr,DefaultBlockParameterName.PENDING).send();
            if (getNonce == null){
                log.error("地址：{}，获取账户的Nonce，bnbGetTransactionCount方法未获取到nonce",addr);
                throw new RuntimeException(addr+"未获取到nonce");
            }
            return getNonce.getTransactionCount();
        } catch (IOException e) {
            log.error("地址：{}，获取账户的Nonce，bnbGetTransactionCount方法未获取到nonce",addr);
            throw new RuntimeException(addr+"获取到nonce异常");
        }
    }

    /**
     * <p>方法说明: 返回当前的gas价格，单位：wei
     * <p>参数说明:
     * <p>返回说明: 数据库存入值 单位Gwei
     * <p>创建时间: 2020/6/17 0017 13:32
     * <p>创  建  人: java-101
     **/
    public static BigInteger bnbGasPrice(Web3j web3j) throws Exception {
        EthGasPrice bnbGasPrice = web3j.ethGasPrice().send();
        BigInteger gasPrice  = bnbGasPrice.getGasPrice();
        return gasPrice ;
    }

    /**
     * <p>方法说明: 对于已经同步的客户端，该调用返回一个描述同步状态的对象；对于未同步客户端，返回false
     * <p>参数说明:
     * <p>返回说明:
     * 对象如下：
     * startingBlock: QUANTITY - 开始块
     * currentBlock: QUANTITY - 当前块，同bnb_blockNumber
     * highestBlock: QUANTITY - 预估最高块
     * <p>创建时间: 2020/6/17 0017 13:33
     * <p>创  建  人: java-101
     **/
    public static boolean bnbSyncing(Web3j web3j) throws Exception {
        EthSyncing bnbSyncing  = web3j.ethSyncing().send();
        boolean isSyncing = bnbSyncing.isSyncing();
        return isSyncing;
    }

    /**
     * <p>方法说明: 获取最近一个区块数(区块高度)
     * <p>参数说明:
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 13:34
     * <p>创  建  人: java-101
     **/
    public static BigInteger getBlockNumber(Web3j web3j) throws Exception {
        EthBlockNumber bnbBlockNumber = web3j.ethBlockNumber().send();
        BigInteger blockNumber = bnbBlockNumber.getBlockNumber();
        log.info("当前区块:{}",blockNumber);
        return blockNumber;
    }

    /**
     * <p>方法说明: 获取指定区块中 交易记录数(count) index从0开始
     * <p>参数说明:
     * <p>返回说明:返回指定块内的交易数量，使用块编号指定块。  "result": "0xa"
     * <p>创建时间: 2020/6/17 0017 13:34
     * <p>创  建  人: java-101
     **/
    public static BigInteger getBlockTransactionCountByNumber(Web3j web3j,BigInteger blockNumber) throws Exception {
        EthGetBlockTransactionCountByNumber bnbGetBlockTransactionCount = web3j.ethGetBlockTransactionCountByNumber(DefaultBlockParameter.valueOf(blockNumber)).send();
        BigInteger count = bnbGetBlockTransactionCount.getTransactionCount();
        return count;
    }

    /**
     * <p>方法说明: 获取指定区块(blockNumber)中指定事务(交易记录,事务index)
     *   SuppressWarnings("unchecked"):为消除这些警告的产生
     * <p>参数说明:
     * <p>返回说明:
     * 		 bnb转账
     *
     * 		 {
     * 		    "jsonrpc": "2.0",
     * 		    "id": 1,
     * 		    "result": {
     * 		        "blockHash": "0xdd5f178275cadaea45af211908afb3a387d154ec91f784f5dc84794aac63d31b",
     * 		        "blockNumber": "0x260f2b",
     * 		        "from": "0xe847380de90060074f53af90fad193f4f94450d3",
     * 		        "gas": "0x5208",
     * 		        "gasPrice": "0x3b9aca00",
     * 		        "hash": "0x961be12561d1df2ee4563cb78867885ace917a5d83e9f74d83c1ee5a54581ac9",
     * 		        "input": "0x", //如果input为0x6f6178时为申请手续费
     * 		        "nonce": "0x354",
     * 		        "to": "0x951fc27402eff842edb9091f81ae799ef1883156",
     * 		        "transactionIndex": "0x5",
     * 		        "value": "0x16345785d8a0000",
     * 		        "v": "0x2b",
     * 		        "r": "0x6ffe12530e7dc3b777c208a06cbf0c6714028a4a075daab49512e81c6f1fc9d5",
     * 		        "s": "0x43a3c4adf9477ec6b7d3e6742e5d57155da2bcd606b7c464877b13fe7f27116f"
     *                        }
     * 		}
     * 		代币
     * 		  {
     * 		    "jsonrpc": "2.0",
     * 		    "id": 1,
     * 		    "result": {
     * 		        "blockHash": "0xe7ca5ad1a806b8b15fb6e980dab1eea184ea47a0cde7fef066f2563925cca226",
     * 		        "blockNumber": "0x2285db",
     * 		        "from": "0x17749c6ce8facd525aa29329eb48b8c24619a9e5",
     * 		        "gas": "0x134f1",
     * 		        "gasPrice": "0x98bca5a00",
     * 		        "hash": "0x34a06a4bfd48759a3fe374175e9ab90168772930af353d1dd7a29bd1ffbf0db7",
     * 		        "input": "0xa9059cbb000000000000000000000000906e1b3dd5c4043f898a583d5fe9ed2b956265850000000000000000000000000000000000000000000000000000000ba43b7400",
     * 		        "nonce": "0x32",
     * 		        "to": "0x9faab12b8b19ba5849fcb3776b2a2d66d0366dc1", -- 代币转账to为合约地址
     * 		        "transactionIndex": "0x0",
     * 		        "value": "0x0",
     * 		        "v": "0x2b",
     * 		        "r": "0x8a9ab10d78e441d0b86dd76a299f19b9285037108e4fccd8455e45864f934d34",
     * 		        "s": "0x32636acd62603e44a03ff4772d349e1bfee0cfb149634f3895e6829a90afd4bc"
     * 		    }
     * 		}
     * <p>创建时间: 2020/6/17 0017 13:36
     * <p>创  建  人: java-101
     *
     * @return*/
    @SuppressWarnings("unchecked")
    public static org.web3j.protocol.core.methods.response.Transaction getTransactionByBlockNumberAndIndex(Web3j web3j, BigInteger blockNumber, BigInteger index) throws Exception {
        EthTransaction bnbTransaction = web3j.ethGetTransactionByBlockNumberAndIndex(DefaultBlockParameter.valueOf(blockNumber),index).send();
        return bnbTransaction.getTransaction().get();
    }

    /**
     * <p>方法说明: 通过 交易hash获取 交易记录
     * <p>返回说明:
     * 返回值
     * Object - 交易对象，如果没有找到匹配的交易则返回null。结构如下：
     *
     * hash: DATA, 32字节 - 交易哈希
     * nonce: QUANTITY - 本次交易之前发送方已经生成的交易数量
     * blockHash: DATA, 32字节 - 交易所在块的哈希，对于挂起块，该值为null
     * blockNumber: QUANTITY - 交易所在块的编号，对于挂起块，该值为null
     * transactionIndex: QUANTITY - 交易在块中的索引位置，挂起块该值为null
     * from: DATA, 20字节 - 交易发送方地址
     * to: DATA, 20字节 - 交易接收方地址，对于合约创建交易，该值为null
     * value: QUANTITY - 发送的以太数量，单位：wei
     * gasPrice: QUANTITY - 发送方提供的gas价格，单位：wei
     * gas: QUANTITY - 发送方提供的gas可用量
     * input: DATA - 随交易发送的数据
     *
     * 		 bnb转账
     * 		  {
     * 		    "jsonrpc": "2.0",
     * 		    "id": 1,
     * 		    "result": {
     * 		        "blockHash": "0xdd5f178275cadaea45af211908afb3a387d154ec91f784f5dc84794aac63d31b",
     * 		        "blockNumber": "0x260f2b",
     * 		        "from": "0xe847380de90060074f53af90fad193f4f94450d3",
     * 		        "gas": "0x5208",
     * 		        "gasPrice": "0x3b9aca00",
     * 		        "hash": "0x961be12561d1df2ee4563cb78867885ace917a5d83e9f74d83c1ee5a54581ac9",
     * 		        "input": "0x",  //如果input为0x6f6178时为申请手续费
     * 		        "nonce": "0x354",
     * 		        "to": "0x951fc27402eff842edb9091f81ae799ef1883156",
     * 		        "transactionIndex": "0x5",
     * 		        "value": "0x16345785d8a0000",
     * 		        "v": "0x2b",
     * 		        "r": "0x6ffe12530e7dc3b777c208a06cbf0c6714028a4a075daab49512e81c6f1fc9d5",
     * 		        "s": "0x43a3c4adf9477ec6b7d3e6742e5d57155da2bcd606b7c464877b13fe7f27116f"
     *                        }
     * 		}
     *
     *
     *
     * 		  代币
     * 		 *{
     * 		    "jsonrpc": "2.0",
     * 		    "id": 1,
     * 		    "result": {
     * 		        "blockHash": "0xe7ca5ad1a806b8b15fb6e980dab1eea184ea47a0cde7fef066f2563925cca226",
     * 		        "blockNumber": "0x2285db",
     * 		        "from": "0x17749c6ce8facd525aa29329eb48b8c24619a9e5",
     * 		        "gas": "0x134f1",
     * 		        "gasPrice": "0x98bca5a00",
     * 		        "hash": "0x34a06a4bfd48759a3fe374175e9ab90168772930af353d1dd7a29bd1ffbf0db7",
     * 		        "input": "0xa9059cbb000000000000000000000000906e1b3dd5c4043f898a583d5fe9ed2b956265850000000000000000000000000000000000000000000000000000000ba43b7400",
     * 		        "nonce": "0x32",
     * 		        "to": "0x9faab12b8b19ba5849fcb3776b2a2d66d0366dc1", -- 代币转账to为合约地址
     * 		        "transactionIndex": "0x0",
     * 		        "value": "0x0",
     * 		        "v": "0x2b",
     * 		        "r": "0x8a9ab10d78e441d0b86dd76a299f19b9285037108e4fccd8455e45864f934d34",
     * 		        "s": "0x32636acd62603e44a03ff4772d349e1bfee0cfb149634f3895e6829a90afd4bc"
     * 		    }
     * 		}
     *
     * //        0xa9059cbb //methodId
     * //        000000000000000000000000
     * //        906e1b3dd5c4043f898a583d5fe9ed2b95626585 //to
     * //        0000000000000000000000000000000000000000000000000000000
     * //        ba43b7400 // qty
     * <p>参数说明:
     * <p>创建时间: 2020/6/17 0017 13:39
     * <p>创  建  人: java-101
     *
     * @return*/
    @SuppressWarnings("unchecked")
    public static org.web3j.protocol.core.methods.response.Transaction getTransactionByHash(Web3j web3j, String txHash) throws Exception {
        EthTransaction bnbTransaction = null;
        org.web3j.protocol.core.methods.response.Transaction returnInfo = null;
        try {
            bnbTransaction = web3j.ethGetTransactionByHash(txHash).send();
            if(bnbTransaction.getResult()!=null){
                returnInfo = bnbTransaction.getTransaction().get();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return returnInfo;
    }

    /**
     * <p>方法说明:获取 以太坊确认的交易
     * 返回指定交易的收据，使用哈希指定交易。
     * 需要指出的是，挂起的交易其收据无效。
     * <p>返回说明:
     * 		 bnb转账
     * 		 {
     * 			    "jsonrpc": "2.0",
     * 			    "id": 1,
     * 			    "result": {
     * 			        "blockHash": "0xdd5f178275cadaea45af211908afb3a387d154ec91f784f5dc84794aac63d31b",
     * 			        "blockNumber": "0x260f2b",
     * 			        "contractAddress": null,
     * 			        "cumulativeGasUsed": "0x48d58",
     * 			        "from": "0xe847380de90060074f53af90fad193f4f94450d3",
     * 			        "gasUsed": "0x5208",
     * 			        "logs": [],
     * 			        "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
     * 			        "status": "0x1",	// status为1表示成功
     * 			        "to": "0x951fc27402eff842edb9091f81ae799ef1883156",
     * 			        "transactionHash": "0x961be12561d1df2ee4563cb78867885ace917a5d83e9f74d83c1ee5a54581ac9",
     * 			        "transactionIndex": "0x5"
     *                                }
     * 			}
     * 		}
     *
     * 		 代币
     * 		 {
     * 			    "jsonrpc": "2.0",
     * 			    "id": 1,
     * 			    "result": {
     * 			        "blockHash": "0xe7ca5ad1a806b8b15fb6e980dab1eea184ea47a0cde7fef066f2563925cca226",
     * 			        "blockNumber": "0x2285db",
     * 			        "contractAddress": null,
     * 			        "cumulativeGasUsed": "0xcdf6",
     * 			        "from": "0x17749c6ce8facd525aa29329eb48b8c24619a9e5",
     * 			        "gasUsed": "0xcdf6",
     * 			        "logs": [
     * 			            {
     * 			                "address": "0x9faab12b8b19ba5849fcb3776b2a2d66d0366dc1",
     * 			                "topics": [
     * 			                    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
     * 			                    "0x00000000000000000000000017749c6ce8facd525aa29329eb48b8c24619a9e5",
     * 			                    "0x000000000000000000000000906e1b3dd5c4043f898a583d5fe9ed2b95626585"
     * 			                ],
     * 			                "data": "0x0000000000000000000000000000000000000000000000000000000ba43b7400",
     * 			                "blockNumber": "0x2285db",
     * 			                "transactionHash": "0x34a06a4bfd48759a3fe374175e9ab90168772930af353d1dd7a29bd1ffbf0db7",
     * 			                "transactionIndex": "0x0",
     * 			                "blockHash": "0xe7ca5ad1a806b8b15fb6e980dab1eea184ea47a0cde7fef066f2563925cca226",
     * 			                "logIndex": "0x0",
     * 			                "removed": false
     * 			            }
     * 			        ],
     * 			        "logsBloom": "0x00000000000000000000000000000000000000000000000000000008000000000000000000000000000000800000000000000000000000000000000000000000000001000000000000000008000000000000000002000000000000000000000000000000040000000000000000000000000200000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000080000400000000000000000000000",
     * 			        "status": "0x1",
     * 			        "to": "0x9faab12b8b19ba5849fcb3776b2a2d66d0366dc1",
     * 			        "transactionHash": "0x34a06a4bfd48759a3fe374175e9ab90168772930af353d1dd7a29bd1ffbf0db7",
     * 			        "transactionIndex": "0x0"
     * 			    }
     * 			}
     * 		}
     *
     *
     * //        0xa9059cbb //methodId
     * //        000000000000000000000000
     * //        906e1b3dd5c4043f898a583d5fe9ed2b95626585 //to
     * //        0000000000000000000000000000000000000000000000000000000
     * //        ba43b7400 // qty
     * <p>参数说明:
     * <p>创建时间: 2020/6/17 0017 13:41
     * <p>创  建  人: java-101
     **/
    @SuppressWarnings("unchecked")
    public static TransactionReceipt getTransactionReceipt(Web3j web3j,String txHash) throws Exception {
        EthGetTransactionReceipt bnbGetTransactionReceipt = web3j.ethGetTransactionReceipt(txHash).send();
        return bnbGetTransactionReceipt.getTransactionReceipt().get();
    }

    /**
     * <p>方法说明: 获取指定地址BNB余额
     * <p>参数说明: address 查询地址
     * <p>参数说明: ”latest”（已经确认了的）, “earliest”（创世区块的） ， “pending”（包含未确认的交易的余额） 原使用pending
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 13:42
     * <p>创  建  人: java-101
     **/
    public static BigDecimal getBNBBalance(Web3j web3j,String address) throws Throwable {
        BigDecimal balance = BigDecimal.ZERO;
        try {
            EthGetBalance bnbGetBalance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();
            if (bnbGetBalance.hasError()) {
                log.error("【获取BNB余额失败，地址: {} ，失败原因：{}】", address, bnbGetBalance.getError().getMessage());
                return balance;
            }
            balance = Convert.fromWei(new BigDecimal(bnbGetBalance.getBalance()), Convert.Unit.ETHER);
            log.info("地址：{}，余额：{} ",address, balance);
        } catch (IOException e) {
            log.error("地址：{}，获取链上余额异常：{}",address,e.getMessage());
        }
        return balance;
    }

    /**
     * <p>方法说明: 获取bnbToken余额
     * <p>参数说明:  address         查询地址
     * <p>参数说明:  contractAddress 合约地址
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 13:42
     * <p>创  建  人: java-101
     **/
    public static BigDecimal getTokenBalance(Web3j web3j,String address, String contractAddress) throws Throwable {
        BigDecimal balanceEnd = BigDecimal.ZERO;
        try {
            Function balanceOf = new Function("balanceOf", Arrays.asList(new Address(address)), Arrays.asList(new TypeReference<Uint256>() {}));
            EthCall bnbCall = web3j.ethCall(Transaction.createEthCallTransaction(address, contractAddress, FunctionEncoder.encode(balanceOf)), DefaultBlockParameterName.PENDING).send();
            if (bnbCall.hasError()) {
                log.error("【获取地址：{}, 合约：{} 余额失败，原因：{}】", address, contractAddress, bnbCall.getError().getMessage());
                return balanceEnd;
            }
            String value = bnbCall.getValue();
            if(value.equals("0x")){
                log.error("【获取地址：{}, 合约：{} 余额失败，原因：{}】", address, contractAddress, "合约地址不正确");
                return balanceEnd;
            }
            String balance = Numeric.toBigInt(value).toString();
            int decimal = getTokenDecimal(web3j,contractAddress);
            balanceEnd = new BigDecimal(balance).divide(new BigDecimal(10).pow(decimal)).setScale(decimal,BigDecimal.ROUND_DOWN);
            log.info("balance = " + balanceEnd);
        } catch (Exception e) {
            log.error("【获取地址：{}, 合约：{}，余额异常：{}】", address, contractAddress, e.getLocalizedMessage());
        }
        return balanceEnd;
    }

    /**
     * <p>方法说明: 获取代币精度
     * <p>参数说明: contractAddress 代币合约地址
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 18:11
     * <p>创  建  人: java-101
    **/
    public static int getTokenDecimal(Web3j web3j,String contractAddress) throws Exception {
        Function function = new Function("decimals", Arrays.asList(), Arrays.asList(new TypeReference<Uint8>() {
        }));
        EthCall bnbCall = web3j.ethCall(Transaction.createEthCallTransaction("0x0000000000000000000000000000000000000000", contractAddress, FunctionEncoder.encode(function)), DefaultBlockParameterName.LATEST).send();
        if (bnbCall.hasError()) {
            log.error("【获取合约：{}，Token 精度失败，失败原因：{}】", contractAddress, bnbCall.getError().getMessage());
            throw new Exception(bnbCall.getError().getMessage());
        }
        List<Type> decode = FunctionReturnDecoder.decode(bnbCall.getValue(), function.getOutputParameters());
        int decimals = Integer.parseInt(decode.get(0).getValue().toString());
        log.info("【获取合约：{} ，精度：decimals = {}】",contractAddress, decimals);
        return decimals;
    }

    /**
     * <p>方法说明: 估算手续费上限
     * <p>参数说明:如果没有指定gas用量上限，geth将使用挂起块的gas上限。 在这种情况下，返回的gas估算量可能不足以执行实际的交易。
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 19:45
     * <p>创  建  人: java-101
    **/
    public static BigInteger getTransactionGasLimit(Web3j web3j, Transaction transaction) {
        try {
            EthEstimateGas bnbEstimateGas = web3j.ethEstimateGas(transaction).send();
            if (bnbEstimateGas.hasError()){
                throw new RuntimeException(bnbEstimateGas.getError().getMessage());
            }
            return bnbEstimateGas.getAmountUsed().multiply(new BigInteger("2"));
        } catch (IOException e) {
            log.error("估算手续上线异常：{}",e.getLocalizedMessage());
            throw new RuntimeException("估算手续费上线异常："+e.getLocalizedMessage());
        }
    }

    /**
     * <p>方法说明: 对交易签名，并发送交易
     * <p>参数说明: data 为扩展参数，随便填写，不能为null
     * <p>返回说明:
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 19:58
     * <p>创  建  人: java-101
    **/
    public static String signAndSend(Web3j web3j, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String to, BigInteger value, String data, byte chainId, String privateKey,String fromAddr) {
        String txHash = "";
        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, to, value, data);
        if (privateKey.startsWith("0x")){
            privateKey = privateKey.substring(2);
        }

        ECKeyPair ecKeyPair = ECKeyPair.create(new BigInteger(privateKey, 16));
        Credentials credentials = Credentials.create(ecKeyPair);

        byte[] signMessage;
        // 主网是1 responst测试网是3  具体查看ChainId
        if (chainId > ChainId.NONE){
            signMessage = TransactionEncoder.signMessage(rawTransaction, chainId, credentials);
        } else {
            signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        }

        String signData = Numeric.toHexString(signMessage);
        if (!"".equals(signData)) {
            try {
                EthSendTransaction send = web3j.ethSendRawTransaction(signData).send();
                txHash = send.getTransactionHash();
                System.out.println(JSON.toJSONString(send));
            } catch (IOException e) {
                log.error("发送交易异常，转出转入地址：【{}】-【{}】",fromAddr,to);
                throw new RuntimeException("发送交易异常");
            }
        }
        return txHash;
    }

    /**
     * <p>方法说明: bnb转账
     * <p>参数说明: contractAddress 合约地址  为null时,为bnb转账
     * <p>参数说明: methodId        合约方法  为null时,为bnb转账
     * <p>参数说明: fromAddr        转出地址
     * <p>参数说明: toAddr          转入地址
     * <p>参数说明: amount          转账数量
     * <p>参数说明: password        from密码
     * <p>返回说明: 转账成功返回txHashId
     * <p>创建时间: 2020/6/17 0017 13:45
     * <p>创  建  人: java-101
     **/
    public static Map<String, Object> transferBNB(Web3j web3j, String fromAddr, String toAddr, BigDecimal amount,String privateKey) throws Exception {

        Map<String, Object> param = null;
        BigInteger nonce = null;
        BigInteger value = null;
        BigInteger gasLimit = null;
        try {
            BigInteger bnbGasPrice = bnbGasPrice(web3j);
            BigInteger gasPrice = BigInteger.valueOf(Math.round(bnbGasPrice.doubleValue() * 1.5));
            // 获得nonce
            nonce = getNonce(web3j, fromAddr);
            // value 转换
            value = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger();

            // 构建交易
            Transaction transaction = Transaction.createEtherTransaction(fromAddr, nonce, gasPrice, null, toAddr, value);
            // 计算gasLimit
            gasLimit = getTransactionGasLimit(web3j, transaction);

            // 查询调用者余额，检测余额是否充足
            BigDecimal bnbBalance = getBNBBalance(web3j, fromAddr);
            BigDecimal balance = Convert.toWei(bnbBalance, Convert.Unit.ETHER);
            // balance < amount + gasLimit ??
            if (balance.compareTo(amount.add(new BigDecimal(gasLimit.toString()))) < 0) {
                log.error("余额不足，转出转入地址：【{}】-【{}】，当前余额：{}，所需手续费估算：{}，转账数量：{}",fromAddr,toAddr,balance,gasLimit.toString(),amount);
                throw new RuntimeException("余额不足，请核实");
            }
            String txHash = signAndSend(web3j, nonce, gasPrice, gasLimit, toAddr, value, "transferBNB",  ChainId.NONE, privateKey,fromAddr);
            if(StringUtils.isNotEmpty(txHash)){
                param = new HashMap<String, Object>();
                log.info("--BNB转账成功-txHashId:{}--", txHash);
                param.put("from", fromAddr);
                param.put("to", toAddr);
                param.put("value", amount.stripTrailingZeros().toEngineeringString());
                param.put("gasPrice",gasPrice);
                param.put("gas", gasLimit);
                param.put("txHashId", txHash);
            }
        } catch (Throwable e) {
            log.error("BNB转账异常，转出转入地址：【{}】-【{}】，异常信息：{}",fromAddr,toAddr,e.getLocalizedMessage());
        }
        return param;

    }

    /**
     * <p>方法说明: BNBToken转账
     * <p>参数说明: contractAddress 合约地址
     * <p>参数说明:  methodId        合约方法
     * <p>参数说明:  from            转出地址
     * <p>参数说明:  to              转入地址
     * <p>参数说明:  qty             转账金额(0x +  十六进制)wei单位 18
     * <p>参数说明:  password        from密码
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 13:44
     * <p>创  建  人: java-101
     **/
    public static Map<String, Object> transferToken(Web3j web3j,String contractAddress, String fromAddr, String toAddr, BigDecimal amount, String privateKey) throws Exception {

        Map<String, Object> param = null;

        try {

            // 获得余额
            BigDecimal bnbBalance = getBNBBalance(web3j, fromAddr);
            BigDecimal tokenBalance = getTokenBalance(web3j, fromAddr, contractAddress);
            BigInteger balance = Convert.toWei(bnbBalance, Convert.Unit.ETHER).toBigInteger();

            int decimal = getTokenDecimal(web3j,contractAddress);//合约精度

            if (bnbBalance.compareTo(BigDecimal.ZERO) <= 0) {
                log.error("BNB_TOKEN 代币合约：{}，转账，转出转入地址：【{}】-【{}】，当前BNB余额：{}，转出地址BNB链上资产必须大于0",contractAddress,fromAddr,toAddr,bnbBalance);
                throw new RuntimeException("转出地址BNB链上资产必须大于0");
            }
            if (tokenBalance.compareTo(amount) < 0) {
                log.error("BNB_TOKEN 代币合约：{}，转账，转出转入地址：【{}】-【{}】，当前BNB_TOKEN余额：{}，转出地址代币链上资产必须大于等于转账金额：{}",contractAddress,fromAddr,toAddr,tokenBalance,amount);
                throw new RuntimeException("转出地址代币【"+contractAddress+"】，链上资产必须大于等于【"+amount+"】");
            }

            BigInteger bnbGasPrice = bnbGasPrice(web3j);
            BigInteger gasPrice = BigInteger.valueOf(Math.round(bnbGasPrice.doubleValue() * 1.5));
            BigInteger nonce = getNonce(web3j, fromAddr);
            // 构建方法调用信息
            String method = "transfer";
            // 构建输入参数
            List<Type> inputArgs = new ArrayList<>();
            inputArgs.add(new Address(toAddr));
            inputArgs.add(new Uint256(amount.multiply(BigDecimal.TEN.pow(decimal)).toBigInteger()));
            // 合约返回值容器
            List<TypeReference<?>> outputArgs = new ArrayList<>();
            String funcABI = FunctionEncoder.encode(new Function(method, inputArgs, outputArgs));

            Transaction transaction = Transaction.createFunctionCallTransaction(fromAddr, nonce, gasPrice, null, contractAddress, funcABI);
            BigInteger gasLimit = getTransactionGasLimit(web3j, transaction);

            if (balance.compareTo(gasLimit) < 0) {
                log.error("BNB_TOKEN 代币合约：{}，转账，手续费不足，转出转入地址：【{}】-【{}】，当前BNB余额：{}，所需手续费估算：{}，转账数量：{}",contractAddress,fromAddr,toAddr,balance,gasLimit.toString(),bnbBalance);
                throw new RuntimeException("手续费不足，请核实");
            }
            if (tokenBalance.compareTo(amount) < 0) {
                log.error("BNB_TOKEN 代币合约：{}，转账，代币不足，转出转入地址：【{}】-【{}】，当前BNB_TOKEN余额：{}，转账数量：{}",contractAddress,fromAddr,toAddr,tokenBalance,amount);
                throw new RuntimeException("代币不足，请核实");
            }

            String txHash = signAndSend(web3j, nonce, gasPrice, gasLimit, contractAddress, BigInteger.ZERO, funcABI, ChainId.NONE, privateKey,fromAddr);
            log.info("--BNB_TOKEN 代币合约：{}，完成转账-txHashId:{}--", contractAddress,txHash);
            if(StringUtils.isNotEmpty(txHash)){
                param = new HashMap<String, Object>();
                param.put("from", fromAddr);
                param.put("to", contractAddress);
                param.put("value", amount.stripTrailingZeros().toEngineeringString());
                param.put("gasPrice",gasPrice);
                param.put("gas", gasLimit);
                param.put("data", funcABI);
                param.put("txHashId", txHash);
            }

        } catch (Throwable e) {
            log.error("BNB_TOKEN代币合约：{}， 转账转账异常，转出转入地址：【{}】-【{}】，异常信息：{}",contractAddress,fromAddr,toAddr,e.getLocalizedMessage());
            throw new RuntimeException("BNB_TOKEN代币合约："+contractAddress+"， 转账转账异常，转出转入地址：【"+fromAddr+"】-【"+toAddr+"】，异常信息："+e.getLocalizedMessage());
        }
        return param;
    }

    /**
     * <p>方法说明: BNB转账 手续费转账  data固定为："0x6f6178"
     * <p>参数说明: from     转出地址
     * <p>参数说明: to       转入地址
     * <p>参数说明: qty      转账金额(0x +  十六进制)wei单位 18
     * <p>参数说明: password from密码
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 13:43
     * <p>创  建  人: java-101
     **/
    public static Map<String, Object> transferBNBForFee(Web3j web3j,String fromAddr, String toAddr, BigDecimal amount, String privateKey) throws Exception {

        Map<String, Object> param = null;
        BigInteger nonce = null;
        BigInteger value = null;
        BigInteger gasLimit = null;
        try {

            BigInteger bnbGasPrice = bnbGasPrice(web3j);
            BigInteger gasPrice = BigInteger.valueOf(Math.round(bnbGasPrice.doubleValue() * 1.5));
            // 获得nonce
            nonce = getNonce(web3j, fromAddr);
            // value 转换
            value = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger();

            // 构建交易
            Transaction transaction = Transaction.createEtherTransaction(fromAddr, nonce, gasPrice, null, toAddr, value);
            // 计算gasLimit
            gasLimit = getTransactionGasLimit(web3j, transaction);

            // 查询调用者余额，检测余额是否充足
            BigDecimal bnbBalance = getBNBBalance(web3j, fromAddr);
            BigDecimal balance = Convert.toWei(bnbBalance, Convert.Unit.ETHER);
            // balance < amount + gasLimit ??
            if (balance.compareTo(amount.add(new BigDecimal(gasLimit.toString()))) < 0) {
                log.error("余额不足，转出转入地址：【{}】-【{}】，当前余额：{}，所需手续费估算：{}，转账数量：{}",fromAddr,toAddr,balance,gasLimit.toString(),amount);
                throw new RuntimeException("余额不足，请核实");
            }

            String txHash = signAndSend(web3j, nonce, gasPrice, gasLimit, toAddr, value, "0x6f6178",  ChainId.NONE, privateKey,fromAddr);
            if(StringUtils.isNotEmpty(txHash)) {
                param = new HashMap<String, Object>();
                log.info("--BNB 手续费完成转账-txHashId:{}--", txHash);
                param.put("from", fromAddr);
                param.put("to", toAddr);
                param.put("value", amount.stripTrailingZeros().toEngineeringString());
                param.put("gasPrice",gasPrice);
                param.put("gas", gasLimit);
                param.put("txHashId", txHash);
            }
        } catch (Throwable e) {
            log.error("BNB 手续费转账异常，转出转入地址：【{}】-【{}】，异常信息：{}",fromAddr,toAddr,e.getLocalizedMessage());
        }

        return param;
    }


    /**
     * <p>方法说明: BNB/Token获取建议 gaslimit
     * <p>参数说明: contractAddress 合约地址  为null时,为bnb转账
     * <p>参数说明: methodId        合约方法  为null时,为bnb转账
     * <p>参数说明: from            转出地址
     * <p>参数说明: to              转入地址
     * <p>参数说明: qty             转账金额(0x +  十六进制)wei单位 18
     * <p>参数说明: gasLimit        最大限制(0x +  十六进制)
     * <p>参数说明: gasPrice        手续费单价(0x + 十六进制)Gwei单位 9
     * <p>返回说明:
     * <p>创建时间: 2020/6/17 0017 13:47
     * <p>创  建  人: java-101
     **/
    public static BigInteger getGasLimit(Web3j web3j,
                              String contractAddress,
                              String fromAddr,
                              String toAddr,
                              BigDecimal amount) throws Exception {
        try {
            Transaction transaction = null;
            if(StringUtils.isNotEmpty(contractAddress)){//代币

                // 获得余额
                BigDecimal bnbBalance = getBNBBalance(web3j, fromAddr);
                BigDecimal tokenBalance = getTokenBalance(web3j, fromAddr, contractAddress);
                int decimal = getTokenDecimal(web3j,contractAddress);//合约精度
                if (bnbBalance.compareTo(BigDecimal.ZERO) <= 0) {
                    log.error("估算手续上限异常，转出地址【"+fromAddr+"】，合约【"+contractAddress+"】，链上BNB资产必须大于0");
                    throw new RuntimeException("估算手续上限异常，转出地址【"+fromAddr+"】，合约【"+contractAddress+"】，链上BNB资产必须大于0");
                }
                if (tokenBalance.compareTo(amount) < 0) {
                    log.error("估算手续上限异常，转出地址【"+fromAddr+"】，合约【"+contractAddress+"】，链上代币资产必须大于等于转账金额【"+amount+"】");
                    throw new RuntimeException("估算手续上限异常，转出地址【"+fromAddr+"】，合约【"+contractAddress+"】，链上代币资产必须大于等于转账金额【"+amount+"】");
                }

                BigInteger nonce = getNonce(web3j, fromAddr);
                BigInteger bnbGasPrice = bnbGasPrice(web3j);
                BigInteger gasPrice = BigInteger.valueOf(Math.round(bnbGasPrice.doubleValue() * 1.5));
                // 构建方法调用信息
                String method = "transfer";
                 // 构建输入参数
                List<Type> inputArgs = new ArrayList<>();
                inputArgs.add(new Address(toAddr));
                inputArgs.add(new Uint256(amount.multiply(BigDecimal.TEN.pow(decimal)).toBigInteger()));
                // 合约返回值容器
                List<TypeReference<?>> outputArgs = new ArrayList<>();
                String funcABI = FunctionEncoder.encode(new Function(method, inputArgs, outputArgs));
                //构建交易
                transaction = Transaction.createFunctionCallTransaction(fromAddr, nonce, gasPrice, null, contractAddress, funcABI);
            }else{
                BigInteger bnbGasPrice = bnbGasPrice(web3j);
                BigInteger gasPrice = BigInteger.valueOf(Math.round(bnbGasPrice.doubleValue() * 1.5));
                // 获得nonce
                BigInteger nonce = getNonce(web3j, fromAddr);
                // value 转换
                BigInteger value = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger();

                // 构建交易
                transaction = Transaction.createEtherTransaction(fromAddr, nonce, gasPrice, null, toAddr, value);
            }

            BigInteger GasLimit = getTransactionGasLimit(web3j,transaction);
            log.info("-----获取gaslimit:{}-----", GasLimit);
            return GasLimit;
        } catch (IOException e) {
            log.error("估算手续上限异常：{}",e.getLocalizedMessage());
            throw new RuntimeException("估算手续上限异常："+e.getLocalizedMessage());
        } catch (Throwable e) {
            log.error("估算手续上限异常，获取转出地址（代币）余额，异常信息：{}",e.getLocalizedMessage());
            throw new RuntimeException("估算手续上限异常，获取转出地址余额，异常信息："+e.getLocalizedMessage());
        }
    }

    /**
     * <p>方法说明: 实体对象转Map对象
     * <p>参数说明:
     * <p>返回说明:
     * <p>创建时间: 2020/6/18 0018 15:04
     * <p>创  建  人: java-101
    **/
    public static Map<String, String> entityToMap(Object object) {
        Map<String, String> map = new HashMap();
        for (Field field : object.getClass().getDeclaredFields()){
            try {
                boolean flag = field.isAccessible();
                field.setAccessible(true);
                Object o = field.get(object);
                map.put(field.getName(), String.valueOf(o));
                field.setAccessible(flag);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    /**
     * <p>方法说明: 创建新帐户 Bip39协议
     * <p>参数说明: pwd 新密码
     * <p>返回说明:
     *      mnemonic :助记词
     *      privateKey：私钥
     *      publicKey：公钥
     *      address：地址
     *      accountKeystorePath：账户文件路径
     * <p>创建时间: 2020/6/17 0017 13:47
     * <p>创  建  人: java-101
     **/
    public static Map<String,String> createAddress(String pwd) {
        Map<String,String> resultMap = new LinkedHashMap();

        StringBuilder sb = new StringBuilder();
        byte[] entropy = new byte[Words.TWELVE.byteLength()];
        new SecureRandom().nextBytes(entropy);
        new MnemonicGenerator(English.INSTANCE).createMnemonic(entropy, sb::append);
        String mnemonic = sb.toString();

        List mnemonicList = Arrays.asList(mnemonic.split(" "));
        byte[] seed = new SeedCalculator().withWordsFromWordList(English.INSTANCE).calculateSeed(mnemonicList, pwd);
        ECKeyPair ecKeyPair = ECKeyPair.create(Sha256.sha256(seed));
        String privateKey = ecKeyPair.getPrivateKey().toString(16);
        String publicKey = ecKeyPair.getPublicKey().toString(16);
        String address = "0x" + Keys.getAddress(publicKey);

        String filePath = System.getProperty("user.home") + File.separator + "keystore";
        //创建钱包地址与密钥
        String fileName = null;
        try {
            fileName = WalletUtils.generateWalletFile(pwd, ecKeyPair, new File(filePath), false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (fileName == null) {
            return null;
        }
        String accountKeystorePath = filePath + File.separator + fileName;

        resultMap.put("mnemonic", mnemonic);
        resultMap.put("privateKey", privateKey);
        resultMap.put("publicKey", publicKey);
        resultMap.put("address", address);
        resultMap.put("accountKeystorePath", accountKeystorePath);
        return resultMap;
    }

    /**
     * <p>方法说明: 创建新帐户 Bip44协议  通用的以太坊基于bip44协议的助记词路径 （imtoken jaxx Metamask myetherwallet）
     * <p>参数说明: pwd 新密码
     * <p>返回说明:
     *      mnemonic :助记词
     *      privateKey：私钥
     *      publicKey：公钥
     *      address：地址
     *      accountKeystorePath：账户文件路径
     * <p>创建时间: 2020/6/17 0017 13:47
     * <p>创  建  人: java-101
     **/
    public static Map<String,String> createAddressForBip44(String pwd) {
        Map<String,String> resultMap = new LinkedHashMap();
        List<String> words = null;
        try {
            words = Bip44Utils.generateMnemonicWords();
        } catch (Exception e) {
            log.error("使用Bip44协议获取通用助记词异常：{}",e.getMessage());
            return null;
        }

        if(words==null){
            return null;
        }
        String mnemonic ="";
        for (String str:words) {
            if(StringUtils.isBlank(mnemonic)){
                mnemonic = str;
            }else{
                mnemonic = mnemonic + " "+str;
            }
        }

        //协议类型
        BigInteger prieth = Bip44Utils.getPathPrivateKey(words,"m/44'/60'/0'/0/0");
        ECKeyPair ecKeyPair = ECKeyPair.create(prieth);

        String privateKey = ecKeyPair.getPrivateKey().toString(16);
        String publicKey = ecKeyPair.getPublicKey().toString(16);
        String address = "0x" + Keys.getAddress(publicKey);

        String filePath = System.getProperty("user.home") + File.separator + "keystore";
        //创建钱包地址与密钥
        String fileName = null;
        try {
            fileName = WalletUtils.generateWalletFile(pwd, ecKeyPair, new File(filePath), false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (fileName == null) {
            return null;
        }
        String accountKeystorePath = filePath + File.separator + fileName;

        resultMap.put("mnemonic", mnemonic);
        resultMap.put("privateKey", privateKey);
        resultMap.put("publicKey", publicKey);
        resultMap.put("address", address);
        resultMap.put("accountKeystorePath", accountKeystorePath);
        return resultMap;
    }

    /**
     * <p>方法说明: 根据 私钥 导入
     * <p>参数说明: privateKey 私钥信息  需要导入的私钥，去掉 '0x' 前缀
     * <p>参数说明: password 对应新建密码
     * <p>返回说明:
     * <p>创建时间: 2020/6/15 0015 19:21
     * <p>创  建  人: java-101
     **/
    public static Map<String,String> importPrivateKey(String privateKey, String password){
        Map<String,String> resultMap = new LinkedHashMap();
        //转化为明文私钥
        BigInteger bigInteger = new BigInteger(privateKey, 16);
        ECKeyPair ecKeyPair = ECKeyPair.create(bigInteger);
        String publicKey = ecKeyPair.getPublicKey().toString(16);
        String address = "0x" + Keys.getAddress(publicKey);

        String filePath = System.getProperty("user.home") + File.separator + "keystore";
        //创建钱包地址与密钥
        String keystoreName = null;
        try {
            keystoreName = WalletUtils.generateWalletFile(password, ecKeyPair, new File(filePath), true);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (keystoreName == null) {
            return null;
        }
        String accountKeystorePath = filePath + File.separator + keystoreName;
        resultMap.put("privateKey", privateKey);
        resultMap.put("publicKey", publicKey);
        resultMap.put("address", address);
        resultMap.put("accountKeystorePath", accountKeystorePath);
        return resultMap;
    }

    /**
     * <p>方法说明: 根据 助记词 导入 Bip39协议
     * <p>参数说明: pwd       新钱包密码
     * <p>参数说明: mnemonic  助记词
     * <p>返回说明:
     * <p>创建时间: 2020/6/18 0018 16:08
     * <p>创  建  人: java-101
    **/
    public static Map<String,String> importByMnemonic(String pwd, String mnemonic) {
        Map<String,String> resultMap = new LinkedHashMap();

        List mnemonicList = Arrays.asList(mnemonic.split(" "));
        byte[] seed = new SeedCalculator().withWordsFromWordList(English.INSTANCE).calculateSeed(mnemonicList, pwd);
        ECKeyPair ecKeyPair = ECKeyPair.create(Sha256.sha256(seed));
        String privateKey = ecKeyPair.getPrivateKey().toString(16);
        String publicKey = ecKeyPair.getPublicKey().toString(16);
        String address = "0x" + Keys.getAddress(publicKey);

        String filePath = System.getProperty("user.home") + File.separator + "keystore";
        //创建钱包地址与密钥
        String fileName = null;
        try {
            fileName = WalletUtils.generateWalletFile(pwd, ecKeyPair, new File(filePath), false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (fileName == null) {
            return null;
        }
        String accountKeystorePath = filePath + File.separator + fileName;
        resultMap.put("mnemonic", mnemonic);
        resultMap.put("privateKey", privateKey);
        resultMap.put("publicKey", publicKey);
        resultMap.put("address", address);
        resultMap.put("accountKeystorePath", accountKeystorePath);
        return resultMap;
    }

    /**
     * <p>方法说明: 根据 助记词 导入 Bip44协议 通用的以太坊基于bip44协议的助记词路径 （imtoken jaxx Metamask myetherwallet）
     * <p>参数说明: pwd       新钱包密码
     * <p>参数说明: mnemonic  助记词
     * <p>返回说明:
     * <p>创建时间: 2020/6/18 0018 16:08
     * <p>创  建  人: java-101
     **/
    public static Map<String,String> importByMnemonicForBip44(String pwd, String mnemonic) {
        Map<String,String> resultMap = new LinkedHashMap();

        String[] mumericArr = mnemonic.split(" ");
        List<String> words = new ArrayList<>();
        for (int i=0;i<mumericArr.length;i++){
            words.add(mumericArr[i]);
        }

        BigInteger prieth = Bip44Utils.getPathPrivateKey(words,"m/44'/60'/0'/0/0");
        ECKeyPair ecKeyPair = ECKeyPair.create(prieth);
        String privateKey = ecKeyPair.getPrivateKey().toString(16);
        String publicKey = ecKeyPair.getPublicKey().toString(16);
        String address = "0x" + Keys.getAddress(publicKey);

        String filePath = System.getProperty("user.home") + File.separator + "keystore";
        //创建钱包地址与密钥
        String fileName = null;
        try {
            fileName = WalletUtils.generateWalletFile(pwd, ecKeyPair, new File(filePath), false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (fileName == null) {
            return null;
        }
        String accountKeystorePath = filePath + File.separator + fileName;
        resultMap.put("mnemonic", mnemonic);
        resultMap.put("privateKey", privateKey);
        resultMap.put("publicKey", publicKey);
        resultMap.put("address", address);
        resultMap.put("accountKeystorePath", accountKeystorePath);
        return resultMap;
    }

    /**
     * <p>方法说明: 导出keystore文件（导出账户）
     * <p>参数说明: walletFilePath  账户完整路径，包括文件名 （accountKeystorePath值）
     * <p>参数说明: password  密码 创建地址的密码
     * <p>返回说明:walletFilew   文件内容
     * <p>返回说明:fileName      文件名称
     * <p>创建时间: 2020/6/18 0018 16:17
     * <p>创  建  人: java-101
    **/
    public static Map<String,Object> export(String walletFilePath, String password) throws IOException, CipherException {
        Map<String,Object> resultMap = new LinkedHashMap();
        Credentials credentials = WalletUtils.loadCredentials(password, walletFilePath);
        ECKeyPair ecKeyPair = credentials.getEcKeyPair();
        boolean useFullScrypt = false;
        WalletFile walletFile = null;
        try {
            if (useFullScrypt) {
                walletFile = Wallet.createStandard(password, ecKeyPair);
            } else {
                walletFile = Wallet.createLight(password, ecKeyPair);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String fileNameEx = getWalletFileName(walletFile);
        log.info("导出keystore文件，文件内容：{}",JSON.toJSONString(walletFile));
        log.info("导出keystore文件，文件名称：{}",fileNameEx);
        resultMap.put("walletFile", walletFile);
        resultMap.put("fileName", fileNameEx);
        return resultMap;
    }

    /**
     * <p>方法说明: 导出keystore文件（导出账户） 处理文件名称
     * <p>参数说明:
     * <p>返回说明:
     * <p>创建时间: 2020/6/18 0018 16:23
     * <p>创  建  人: java-101
    **/
    public static String getWalletFileName(WalletFile walletFile){
        DateTimeFormatter format = DateTimeFormatter.ofPattern( "'UTC--'yyyy-MM-dd'T'HH-mm-ss.nVV'--'");
        ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
        return now.format(format) + walletFile.getAddress() + ".json";
    }
}
